home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 April / macformat-023.iso / Shareware City / Developers / SliderCDEF1.3.0 / SliderCDEF1.3.0.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-11-20  |  48.7 KB  |  1,695 lines  |  [TEXT/MMCC]

  1. // =================================================================================
  2. //    SliderCDEF1.3.0.c                            ©1993-1994 Harold Ekstrom. All rights reserved.
  3. // =================================================================================
  4. //    SliderCDEF.h
  5.  
  6. #include <Picker.h>
  7. #include <TextUtils.h>
  8.  
  9. #include "SliderCDEF.h"
  10.  
  11. // ---------------------------------------------------------------------------------
  12. //    • main for SliderCDEF
  13. // ---------------------------------------------------------------------------------
  14. //    The entry point to the control definition. This function is called directly
  15. //    by the control manager for all control actions.
  16.  
  17. pascal long
  18. main( short inVarCode, ControlHandle inControl, short inMessage, long inParam )
  19. {
  20.     // Initialize the result to zero.
  21.     long    theResult = 0L;
  22.  
  23.     // Handle the message.
  24.     switch( inMessage ) {
  25.     
  26.         case drawCntl:
  27.             // According to tech note TB 31, the high word of
  28.             // inParam may contain undefined data.
  29.             inParam = LoWord( inParam );
  30.             
  31.             // If the control is visible, draw it.
  32.             if ( (**inControl).contrlVis ) {
  33.                 SliderDrawControl( inVarCode, inControl, inParam );
  34.             }
  35.             break;
  36.             
  37.         case testCntl:
  38.             theResult = SliderTestControl( inVarCode, inControl, inParam );
  39.             break;
  40.             
  41.         case calcCRgns:
  42.             SliderCalcCRgns( inVarCode, inControl, inParam );
  43.             break;
  44.             
  45.         case initCntl:
  46.             // Comment the following line when compiling as stand-alone code.
  47.             // (**inControl).contrlRfCon = (long) SliderCDEF;
  48.  
  49.             SliderInitControl( inVarCode, inControl, inParam );
  50.             break;
  51.             
  52.         case dispCntl:
  53.             SliderDisposeControl( inVarCode, inControl, inParam );
  54.             break;
  55.             
  56.         case posCntl:
  57.             // Not used for a control that does default dragging.
  58.             break;
  59.             
  60.         case thumbCntl:
  61.             // Not used for a control that does default dragging.
  62.             break;
  63.             
  64.         case dragCntl:
  65.             theResult = SliderDragControl( inVarCode, inControl, inParam );
  66.             break;
  67.             
  68.         case autoTrack:
  69.             // According to tech note TB 31, the high word of
  70.             // inParam may contain undefined data.
  71.             inParam = LoWord( inParam );
  72.             SliderAutoTrack( inVarCode, inControl, inParam );
  73.             break;
  74.             
  75.         case calcCntlRgn:
  76.             SliderCalcCntlRgn( inVarCode, inControl, inParam );
  77.             break;
  78.             
  79.         case calcThumbRgn:
  80.             SliderCalcThumbRgn( inVarCode, inControl, inParam );
  81.             break;
  82.     }
  83.  
  84.     return theResult;
  85. }
  86.  
  87.  
  88. // ---------------------------------------------------------------------------------
  89. //    • SliderDrawControl
  90. // ---------------------------------------------------------------------------------
  91. //    The main drawing routine for the CDEF. Called in respone
  92. //    to the drawCtrl message if the control is visible.
  93. //    Saves port characteristics, determines if the slider needs to
  94. //    be redrawn in its offscreen pixmap, redraws if necessary,
  95. //    copies the control to the window, and restores the port.
  96.  
  97. void
  98. SliderDrawControl( short inVarCode, ControlHandle inControl, long inParam )
  99. {
  100.     SliderDataHnd            theSliderData;
  101.     GrafPtr                theSavedPort;
  102.     PenState                theSavedPenState;
  103.     RGBColor                theForeColor, theBackColor;
  104.     RgnHandle                theSavedClip;
  105.     DeviceLoopDrawingUPP    theDeviceLoopUPP;
  106.     GWorldFlags            theGWFlags;
  107.     short                theCurrDepth;
  108.     short                thePosition;
  109.     RgnHandle                theVisRgn;
  110.     
  111.     // Check the slider's control handle.
  112.     if ( inControl == nil ) return;
  113.     
  114.     // Get the slider's data.
  115.     theSliderData = GetSliderData( inControl );
  116.     if ( theSliderData == nil ) return;
  117.     
  118.     // Save the pen state.
  119.     GetPenState( &theSavedPenState );
  120.  
  121.     // Save the foreground and background colors.
  122.     GetForeColor( &theForeColor );
  123.     GetBackColor( &theBackColor );
  124.     
  125.     // Save the current clip region.
  126.     theSavedClip = NewRgn();
  127.     GetClip( theSavedClip );
  128.  
  129.     // Save the current port.
  130.     GetPort( &theSavedPort );
  131.     
  132.     // Calculate the new thumb position and put it in the slider's data.
  133.     thePosition = CalcPosition( inControl );
  134.     (**theSliderData).sdPosition = thePosition;
  135.  
  136.     // Find out if the depth has changed and update the offscreen
  137.     // graphics worlds if necessary.
  138.     theCurrDepth = GetDepth( inControl );
  139.     if ( (**theSliderData).sdDepth != theCurrDepth ) {
  140.  
  141.         // Lock the slider data.
  142.         HLock( (Handle) theSliderData );
  143.     
  144.         // Update the track offscreen graphics world.
  145.         theGWFlags = UpdateGWorld( &(**theSliderData).sdTrack, theCurrDepth,
  146.             &(**theSliderData).sdTrack->portRect, 0, nil, 0 );
  147.  
  148.         // Update the thumb offscreen graphics world.
  149.         theGWFlags = UpdateGWorld( &(**theSliderData).sdThumb, theCurrDepth,
  150.             &(**theSliderData).sdThumb->portRect, 0, nil, 0 );
  151.  
  152.         // Keep track of the current depth.
  153.         (**theSliderData).sdDepth = theCurrDepth;
  154.  
  155.         // Unlock the slider data.
  156.         HUnlock( (Handle) theSliderData );
  157.     
  158.     }
  159.  
  160.     // Draw the control's track and the control's indicator
  161.     // in the offscreen graphics world.
  162.     theDeviceLoopUPP = NewDeviceLoopDrawingProc( SliderDrawParts );
  163.     theVisRgn = ((GrafPtr)(**inControl).contrlOwner)->visRgn;
  164.     DeviceLoop( theVisRgn, theDeviceLoopUPP, (long) inControl, 0 );
  165.     DisposeRoutineDescriptor( theDeviceLoopUPP );
  166.  
  167.     // Draw the control. Copy it from the offscreen pixmap to the window.
  168.     theDeviceLoopUPP = NewDeviceLoopDrawingProc( SliderBlitControl );
  169.     theVisRgn = ((GrafPtr)(**inControl).contrlOwner)->visRgn;
  170.     DeviceLoop( theVisRgn, theDeviceLoopUPP, (long) inControl, 0 );
  171.     DisposeRoutineDescriptor( theDeviceLoopUPP );
  172.  
  173.     // Draw the control's value if needed.
  174.     if ( ( (**theSliderData).sdVarCode & kValueAboveCtl ) ) {
  175.  
  176.         // Restore the foreground and background colors.
  177.         // Drawing the value requires correct window colors.
  178.         RGBForeColor( &theForeColor );
  179.         RGBBackColor( &theBackColor );
  180.     
  181.         // Draw the control value.
  182.         theDeviceLoopUPP = NewDeviceLoopDrawingProc( SliderDrawValue );
  183.         theVisRgn = ((GrafPtr)(**inControl).contrlOwner)->visRgn;
  184.         DeviceLoop( theVisRgn, theDeviceLoopUPP, (long) inControl, 0 );
  185.         DisposeRoutineDescriptor( theDeviceLoopUPP );
  186.  
  187.     }
  188.  
  189.     // Restore the port.
  190.     SetPort( theSavedPort );
  191.             
  192.     // Restore the clipping region.
  193.     SetClip( theSavedClip );
  194.     DisposeRgn( theSavedClip );
  195.  
  196.     // Restore the foreground and background colors.
  197.     RGBForeColor( &theForeColor );
  198.     RGBBackColor( &theBackColor );
  199.     
  200.     // Restore the pen state.
  201.     SetPenState( &theSavedPenState );
  202. }
  203.  
  204.  
  205. // ---------------------------------------------------------------------------------
  206. //    • SliderBlitControl
  207. // ---------------------------------------------------------------------------------
  208. //    Copy the slider to the window. Only done in response to the drawCntl message.
  209. //    The control dragging function performs it's own drawing.
  210.  
  211. pascal void
  212. SliderBlitControl( short inDepth, short inDeviceFlags,
  213.     GDHandle inTargetDevice, long inUserData  )
  214. {
  215.     ControlHandle    theControl;
  216.     SliderDataHnd    theSliderData;
  217.     WindowPtr    theWindow;
  218.     Rect            theRect;
  219.  
  220.     // Get the slider's control handle.
  221.     theControl = (ControlHandle) inUserData;
  222.     if ( theControl == nil ) return;
  223.     
  224.     // Get the slider's data.
  225.     theSliderData = GetSliderData( theControl );
  226.     if ( theSliderData == nil ) return;
  227.     
  228.     // Lock the slider data.
  229.     HLock( (Handle) theSliderData );
  230.     
  231.     // Get the control's parent window.
  232.     theWindow = (**theControl).contrlOwner;
  233.     SetPort( theWindow );
  234.     
  235.     // Set the foreground and background colors for CopyBits.
  236.     ForeColor( blackColor );
  237.     BackColor( whiteColor );
  238.  
  239.     // Calculate the track rect.
  240.     CalcTrackRect( theControl, &theRect );
  241.  
  242.     // Copy the track to the window.
  243.     CopyBits( &((GrafPtr)(**theSliderData).sdTrack)->portBits,
  244.         &((GrafPtr)theWindow)->portBits,
  245.         &(**theSliderData).sdTrackBounds, &theRect, srcCopy, nil );
  246.  
  247.     // Calculate the thumb rect.
  248.     CalcThumbRect( theControl, &theRect );
  249.  
  250.     // Copy the thumb to the window.
  251.     CopyBits( &((GrafPtr)(**theSliderData).sdThumb)->portBits,
  252.         &((GrafPtr)theWindow)->portBits,
  253.         &(**theSliderData).sdThumbBounds, &theRect, srcCopy, nil );
  254.  
  255.     // Unlock the slider data.
  256.     HUnlock( (Handle) theSliderData );
  257. }
  258.  
  259.  
  260. // ---------------------------------------------------------------------------------
  261. //    • SliderDrawParts
  262. // ---------------------------------------------------------------------------------
  263. //    Draw each of the slider's parts into each of the slider's GWorlds.
  264.  
  265. pascal void
  266. SliderDrawParts( short inDepth, short inDeviceFlags,
  267.     GDHandle inTargetDevice, long inUserData  )
  268. {
  269.     ControlHandle    theControl;
  270.     SliderDataHnd    theSliderData;
  271.     GWorldPtr        theCurrPort;
  272.     GDHandle        theCurrDev;
  273.  
  274.     // Get the slider's control handle.
  275.     theControl = (ControlHandle) inUserData;
  276.     if ( theControl == nil ) return;
  277.     
  278.     // Get the slider's data.
  279.     theSliderData = GetSliderData( theControl );
  280.     if ( theSliderData == nil ) return;
  281.     
  282.     // Get the current port and device.
  283.     GetGWorld( &theCurrPort, &theCurrDev );
  284.  
  285.     // Lock the slider data.
  286.     HLock( (Handle) theSliderData );
  287.     
  288.     // Set the port to the track offscreen graphics world.
  289.     SetGWorld ( (**theSliderData).sdTrack, nil );
  290.  
  291.     // Lock the track's pixels.
  292.     LockPixels( GetGWorldPixMap( (**theSliderData).sdTrack ) );
  293.     
  294.     // Draw the track. Use our own depth.
  295.     SliderDrawTrack( (**theSliderData).sdDepth, theControl );
  296.     
  297.     // Unlock the track's pixels.
  298.     UnlockPixels( GetGWorldPixMap( (**theSliderData).sdTrack ) );
  299.     
  300.     // Set the port to the thumb offscreen graphics world.
  301.     SetGWorld ( (**theSliderData).sdThumb, nil );
  302.  
  303.     // Lock the thumb's pixels.
  304.     LockPixels( GetGWorldPixMap( (**theSliderData).sdThumb ) );
  305.     
  306.     // Draw the thumb. Use our own depth.
  307.     SliderDrawThumb( (**theSliderData).sdDepth, theControl );
  308.     
  309.     // Unlock the track's pixels.
  310.     UnlockPixels( GetGWorldPixMap( (**theSliderData).sdThumb ) );
  311.     
  312.     // Done drawing, now reset port.
  313.     SetGWorld ( theCurrPort, theCurrDev );
  314. }
  315.  
  316.  
  317. // ---------------------------------------------------------------------------------
  318. //    • SliderDrawTrack
  319. // ---------------------------------------------------------------------------------
  320. //    Draws the track for the slider.
  321.  
  322. void
  323. SliderDrawTrack( short inDepth, ControlHandle inControl )
  324. {
  325.     SliderDataHnd    theSliderData;
  326.     Rect            theTrackBounds;
  327.     Boolean        isActive;
  328.     Pattern        theGrayPat;
  329.     AuxCtlHandle    theAuxHndl;
  330.     CCTabHandle    theAuxCTable;
  331.     RGBColor        theColor;
  332.  
  333.     // Get out now if theControl is nil.
  334.     if ( inControl == nil ) return;
  335.     
  336.     // Get the slider's data.
  337.     theSliderData = GetSliderData( inControl );
  338.     if ( theSliderData == nil ) return;
  339.  
  340.     // Get a copy of the track bounds (GWorld coordinates).
  341.     theTrackBounds = (**theSliderData).sdTrackBounds;
  342.  
  343.     // Find out if the slider is active.
  344.     isActive = IsSliderActive( inControl );
  345.  
  346.     // Normalize the pen.
  347.     PenNormal();
  348.                         
  349.     switch ( inDepth ) {
  350.     
  351.         case 1:
  352.         case 2:
  353.             ForeColor( blackColor );
  354.             BackColor( whiteColor );
  355.             
  356.             // Erase the track.
  357.             EraseRect( &theTrackBounds );
  358.             
  359.             // Draw in gray pattern if disabled.
  360.             if ( !isActive ) {
  361.                 GetIndPattern( &theGrayPat, sysPatListID, 4 );
  362.                 PenPat( &theGrayPat );
  363.             }
  364.             
  365.             // Frame the track.
  366.             FrameRect( &theTrackBounds );
  367.             break;
  368.             
  369.         default:
  370.             // Get the aux control record.
  371.             GetAuxiliaryControlRecord( inControl, &theAuxHndl );
  372.             
  373.             // Get the color table.
  374.             theAuxCTable = (**theAuxHndl).acCTable;
  375.         
  376.             // Lock the handle to the slider's data.
  377.             HLock( (Handle) theSliderData );
  378.  
  379.             // Find out the slider's activation state and draw accordingly.
  380.             if ( !isActive ) {
  381.             
  382.                 // Fill in the track.
  383.                 theColor.red = theColor.green = theColor.blue = 0xCCCC;
  384.                 RGBForeColor( &theColor );
  385.                 PaintRect( &theTrackBounds );
  386.  
  387.                 // Frame the track.
  388.                 theColor.red = theColor.green = theColor.blue = 0x8888;
  389.                 RGBForeColor( &theColor );
  390.                 FrameRect( &theTrackBounds );
  391.  
  392.             } else {
  393.             
  394.                 // Fill in the track.
  395.                 theColor = ((**theAuxCTable).ctTable[13]).rgb;
  396.                 RGBForeColor( &theColor );
  397.                 PaintRect( &theTrackBounds );
  398.                 
  399.                 // Frame the track.
  400.                 theColor = ((**theAuxCTable).ctTable[0]).rgb;
  401.                 RGBForeColor( &theColor );
  402.                 FrameRect( &theTrackBounds );
  403.                 
  404.                 // Outline the bottom and right sides of the track.
  405.                 theColor = ((**theAuxCTable).ctTable[9]).rgb;
  406.                 RGBForeColor( &theColor );
  407.                 theTrackBounds.bottom -= 1;
  408.                 theTrackBounds.right -= 1;
  409.                 MoveTo( theTrackBounds.left+1, theTrackBounds.bottom-1 );
  410.                 LineTo( theTrackBounds.right-1, theTrackBounds.bottom-1 );
  411.                 LineTo( theTrackBounds.right-1, theTrackBounds.top+1 );
  412.             }
  413.             
  414.             // Unlock the slider's data.
  415.             HUnlock( (Handle) theSliderData );
  416.             break;
  417.     }
  418.     
  419.     if ( ( (**theSliderData).sdVarCode & kTitleInTrack ) && IsHorizontal( inControl ) ) {
  420.         
  421.         // Draw the title inside the slider track for this
  422.         // variation only if the control is horizontal.
  423.         
  424.         // Check for a blank title.
  425.         if ( (**inControl).contrlTitle[0] != 0 ) {
  426.         
  427.             FontInfo    theFInfo;
  428.             short    theFontHeight;
  429.             
  430.             // Set the text traits.
  431.             TextFont( kStdFont );
  432.             TextSize( kStdFontSize );
  433.             TextFace( kStdFontFace );
  434.             
  435.             // Get the font info.
  436.             GetFontInfo( &theFInfo );
  437.             
  438.             // Calculate the font height.
  439.             theFontHeight = theFInfo.ascent + theFInfo.descent;
  440.             
  441.             // Get a fresh copy of the track bounds.
  442.             theTrackBounds = (**theSliderData).sdTrackBounds;
  443.  
  444.             // Check for a track shorter than the standard font height.
  445.             if ( theTrackBounds.bottom - theTrackBounds.top >= theFontHeight ) {
  446.                 
  447.                 Str255    theString;
  448.                 short    theTrackWidth;
  449.                 
  450.                 // Copy the slider title.
  451.                 BlockMoveData( (**inControl).contrlTitle, theString,
  452.                     (**inControl).contrlTitle[0] + 1 );
  453.                 // Calculate the track width.
  454.                 theTrackWidth = theTrackBounds.right - theTrackBounds.left;
  455.                 
  456.                 // Trunctate the string if needed.
  457.                 if ( TruncString( theTrackWidth, theString, truncEnd ) >= 0 ) {
  458.                 
  459.                     short    theHPos;
  460.                     short    theVPos;
  461.                     
  462.                     // Calculate the horizontal position for the text.
  463.                     theHPos = theTrackBounds.left +
  464.                         ( theTrackWidth - StringWidth( theString ) ) / 2;
  465.                         
  466.                     // Calculate the vertical position for the text.
  467.                     theVPos = theTrackBounds.bottom - ( ( theTrackBounds.bottom -
  468.                         theTrackBounds.top ) - theFontHeight ) / 2 - theFInfo.descent - 1;
  469.                     
  470.                     // Move to the text position.    
  471.                     MoveTo( theHPos, theVPos );
  472.                     
  473.                     if ( inDepth >= 8 ) {
  474.                         if ( !isActive ) {
  475.                     
  476.                             // Use a dark gray color.
  477.                             theColor.red = theColor.green = theColor.blue = 0x8888;
  478.                             RGBForeColor( &theColor );
  479.                             
  480.                         } else {
  481.                         
  482.                             // Use black.
  483.                             ForeColor( blackColor );
  484.                             
  485.                         }
  486.                     }
  487.                     
  488.                     // Draw the string.
  489.                     DrawString( theString );
  490.                 }
  491.             }
  492.         }
  493.     }
  494. }
  495.  
  496.  
  497. // ---------------------------------------------------------------------------------
  498. //    • SliderDrawThumb
  499. // ---------------------------------------------------------------------------------
  500. //    Draws the thumb for the slider.
  501.  
  502. void
  503. SliderDrawThumb( short inDepth, ControlHandle inControl )
  504. {
  505.     SliderDataHnd    theSliderData;
  506.     Rect            theThumbBounds;
  507.     short        theThumbCenter;
  508.     short        theThumbWidth;
  509.     Boolean        isHorizontal;
  510.     Pattern        theGrayPat;
  511.     AuxCtlHandle    theAuxHndl;
  512.     CCTabHandle    theAuxCTable;
  513.     RGBColor        theColor;
  514.     RGBColor        theLightColor;
  515.     RGBColor        theDarkColor;
  516.  
  517.     // Get out now if theControl is nil.
  518.     if ( inControl == nil ) return;
  519.     
  520.     // Get the slider's data.
  521.     theSliderData = GetSliderData( inControl );
  522.     if ( theSliderData == nil ) return;
  523.  
  524.     // Get a copy of the thumb bounds (GWorld coordinates).
  525.     theThumbBounds = (**theSliderData).sdThumbBounds;
  526.  
  527.     // Find out if the slider is horizontal or vertical.
  528.     isHorizontal = IsHorizontal( inControl );
  529.     
  530.     // Calculate the thumb width and center.
  531.     if ( isHorizontal ) {
  532.         theThumbWidth = (theThumbBounds.right - theThumbBounds.left);
  533.         theThumbCenter = theThumbBounds.left + theThumbWidth/2 - 1;
  534.     } else {
  535.         theThumbWidth = (theThumbBounds.bottom - theThumbBounds.top);
  536.         theThumbCenter = theThumbBounds.top + theThumbWidth/2 - 1;
  537.     }
  538.  
  539.     // Normalize the pen.
  540.     PenNormal();
  541.                         
  542.     switch ( inDepth ) {
  543.  
  544.         case 1:
  545.         case 2:
  546.             // Drawing in B&W, just frame the track.
  547.             ForeColor( blackColor );
  548.             BackColor( whiteColor );
  549.  
  550.             // Erase the track.
  551.             EraseRect( &theThumbBounds );
  552.  
  553.             if ( !IsSliderActive( inControl ) ) {
  554.  
  555.                 // Draw in gray pattern.
  556.                 GetIndPattern( &theGrayPat, sysPatListID, 4 );
  557.                 PenPat( &theGrayPat );
  558.  
  559.             }
  560.  
  561.             // Frame the thumb.
  562.             FrameRect( &theThumbBounds );
  563.             
  564.             // Draw the thumb centerline.
  565.             if ( isHorizontal ) {
  566.                 MoveTo( theThumbCenter, theThumbBounds.bottom-1 );
  567.                 LineTo( theThumbCenter, theThumbBounds.top );
  568.             } else {
  569.                 MoveTo( theThumbBounds.left+1, theThumbCenter );
  570.                 LineTo( theThumbBounds.right, theThumbCenter );
  571.             }
  572.             break;
  573.             
  574.         default:
  575.             // Get the aux control record.
  576.             GetAuxiliaryControlRecord( inControl, &theAuxHndl );
  577.             
  578.             // Get the color table.
  579.             theAuxCTable = (**theAuxHndl).acCTable;
  580.         
  581.             // Lock the handle to the slider's data.
  582.             HLock( (Handle) theSliderData );
  583.  
  584.             // Find out the slider's state and draw accordingly.
  585.             if ( !IsSliderActive( inControl ) ) {
  586.             
  587.                 // Fill in the thumb rect.
  588.                 theColor.red = theColor.green = theColor.blue = 0xCCCC;
  589.                 RGBForeColor( &theColor );
  590.                 PaintRect( &theThumbBounds );
  591.                 
  592.                 // Frame is with the disabled color.
  593.                 theColor.red = theColor.green = theColor.blue = 0x8888;
  594.                 RGBForeColor( &theColor );
  595.                 FrameRect( &theThumbBounds );
  596.                 
  597.                 // Draw the thumb center line.
  598.                 if ( isHorizontal ) {
  599.                     MoveTo( theThumbCenter, theThumbBounds.bottom-1 );
  600.                     LineTo( theThumbCenter, theThumbBounds.top );
  601.                 } else {
  602.                     MoveTo( theThumbBounds.left, theThumbCenter );
  603.                     LineTo( theThumbBounds.right, theThumbCenter );
  604.                 }
  605.                 
  606.  
  607.             } else {
  608.             
  609.                 // Get the light and dark highlight colors from the control color table.
  610.                 theLightColor = ((**theAuxCTable).ctTable[13]).rgb;
  611.                 theDarkColor = ((**theAuxCTable).ctTable[14]).rgb;
  612.             
  613.                 // Find a good dark color based on the highlight colors.
  614.                 MixColors( &theLightColor, &theDarkColor, kThumbShade, &theColor );
  615.  
  616.                 // Fill in the thumb rect.    
  617.                 RGBForeColor( &theColor );
  618.                 PaintRect( &theThumbBounds );
  619.                 
  620.                 // Use the dark color to frame the thumb.
  621.                 RGBForeColor( &theDarkColor );
  622.                 
  623.                 if ( isHorizontal ) {
  624.                 
  625.                     // Outline the left, bottom, right, and center line of the thumb.
  626.                     MoveTo( theThumbBounds.left, theThumbBounds.top );
  627.                     LineTo( theThumbBounds.left, theThumbBounds.bottom-1 );
  628.                     LineTo( theThumbBounds.right-1, theThumbBounds.bottom-1 );
  629.                     LineTo( theThumbBounds.right-1, theThumbBounds.top );
  630.                     MoveTo( theThumbCenter, theThumbBounds.bottom-1 );
  631.                     LineTo( theThumbCenter, theThumbBounds.top );
  632.                     
  633.                 } else {
  634.                 
  635.                     // Outline the top, bottom, right, and center line of the thumb.
  636.                     MoveTo( theThumbBounds.left, theThumbBounds.top );
  637.                     LineTo( theThumbBounds.right-1, theThumbBounds.top );
  638.                     LineTo( theThumbBounds.right-1, theThumbBounds.bottom-1 );
  639.                     LineTo( theThumbBounds.left, theThumbBounds.bottom-1 );
  640.                     MoveTo( theThumbBounds.left, theThumbCenter );
  641.                     LineTo( theThumbBounds.right, theThumbCenter );
  642.                     
  643.                 }
  644.                 
  645.                 // Add some highlights.
  646.                 theColor = ((**theAuxCTable).ctTable[9]).rgb;
  647.                 RGBForeColor( &theColor );
  648.                 if ( isHorizontal ) {
  649.                 
  650.                     MoveTo( theThumbBounds.left+1, theThumbBounds.bottom-2 );
  651.                     LineTo( theThumbBounds.left+1, theThumbBounds.top );
  652.                     LineTo( theThumbCenter-1, theThumbBounds.top );
  653.                     MoveTo( theThumbCenter+1, theThumbBounds.bottom-2 );
  654.                     LineTo( theThumbCenter+1, theThumbBounds.top );
  655.                     LineTo( theThumbBounds.right-2, theThumbBounds.top );
  656.                     
  657.                 } else {
  658.                 
  659.                     MoveTo( theThumbBounds.left, theThumbBounds.bottom-2 );
  660.                     LineTo( theThumbBounds.left, theThumbCenter+1 );
  661.                     LineTo( theThumbBounds.right-2, theThumbCenter+1 );
  662.                     MoveTo( theThumbBounds.left, theThumbCenter-1 );
  663.                     LineTo( theThumbBounds.left, theThumbBounds.top+1 );
  664.                     LineTo( theThumbBounds.right-2, theThumbBounds.top+1 );
  665.                     
  666.                 }
  667.             }
  668.             
  669.             // Unlock the slider's data.
  670.             HUnlock( (Handle) theSliderData );
  671.             break;
  672.     }
  673. }
  674.  
  675.  
  676. // ---------------------------------------------------------------------------------
  677. //    • SliderDrawValue
  678. // ---------------------------------------------------------------------------------
  679. //    Draws the control value centered over the thumb just above or to the left of the
  680. //    slider's rectangle. Note: drawing occurs outside the control's rectangle!
  681.  
  682. pascal void
  683. SliderDrawValue( short inDepth, short inDeviceFlags,
  684.     GDHandle inTargetDevice, long inUserData  )
  685. {
  686.     ControlHandle    theControl;
  687.     SliderDataHnd    theSliderData;
  688.     WindowPtr    theWindow;
  689.     long            theValue;
  690.     short        theStringWidth;
  691.     short        theStringHalfWidth;
  692.     short        theHPos;
  693.     short        theVPos;
  694.     short        theThumbCenter;
  695.     Rect            wThumbRect;
  696.     Rect            theTextRect;
  697.     FontInfo        theFInfo;
  698.     Str255        theValueString;
  699.     short        savedTextFont;
  700.     short        savedTextStyle;
  701.     short        savedTextSize;
  702.     RgnHandle        savedClip;
  703.     RgnHandle        newClip;
  704.     Pattern        grayPattern;
  705.     AuxCtlHandle    acHndl;
  706.     CCTabHandle    acCTable;
  707.     RGBColor        theColor;
  708.  
  709.     // Get the slider's control handle.
  710.     theControl = (ControlHandle) inUserData;
  711.     if ( theControl == nil ) return;
  712.     
  713.     // Get the slider's data.
  714.     theSliderData = GetSliderData( theControl );
  715.     if ( theSliderData == nil ) return;
  716.  
  717.     // Get the control's parent window.
  718.     theWindow = (WindowPtr) (**theControl).contrlOwner;
  719.     SetPort( theWindow );
  720.     
  721.     // Save the current clip region.
  722.     savedClip = NewRgn();
  723.     GetClip( savedClip );
  724.         
  725.     // Save the font characteristics.
  726.     savedTextFont = ((GrafPtr)theWindow)->txFont;
  727.     savedTextStyle = ((GrafPtr)theWindow)->txFace;
  728.     savedTextSize = ((GrafPtr)theWindow)->txSize;
  729.  
  730.     // Get the control's value.
  731.     theValue = (long) (**theControl).contrlValue;
  732.     
  733.     // Turn the value into a string.
  734.     NumToString( theValue, theValueString );
  735.         
  736.     if ( (**theSliderData).sdVarCode & kTitleAfterValue ) {
  737.         
  738.         // Concatenate the control title to the value string.
  739.         ConcatPStr( theValueString, (**theControl).contrlTitle );
  740.  
  741.     }
  742.                 
  743.     if ( (**theSliderData).sdVarCode & kTitleBeforeValue ) {
  744.         
  745.         // Prefix the control title to the value string.
  746.         PrefixPStr( theValueString, (**theControl).contrlTitle );
  747.  
  748.     }
  749.  
  750.     // Set the text attributes.
  751.     TextFont( kStdFont );
  752.     TextSize( kStdFontSize );
  753.     TextFace( kStdFontFace );
  754.  
  755.     // Calculate the string width and half width.
  756.     theStringWidth = StringWidth( theValueString );
  757.     theStringHalfWidth = theStringWidth / 2;
  758.     
  759.     // Get the font information structure.
  760.     GetFontInfo( &theFInfo );
  761.  
  762.     // Get the thumb rectangle (window coordinates).
  763.     CalcThumbRect( theControl, &wThumbRect );
  764.     
  765.     // Set the text rectangle to the control's rectangle.
  766.     theTextRect = (**theControl).contrlRect;
  767.                     
  768.     if ( IsHorizontal( theControl ) ) {
  769.     
  770.         // Calculate the thumb center.
  771.         theThumbCenter = wThumbRect.left+(wThumbRect.right-wThumbRect.left)/2;
  772.  
  773.         // Calculate the horizontal position of the text.
  774.         theHPos = theThumbCenter - theStringHalfWidth;
  775.         
  776.         // Calculate the vertical position of the text.
  777.         theVPos = (**theControl).contrlRect.top - 2;
  778.         
  779.         // Calculate the top of the text rectangle.
  780.         theTextRect.bottom = (**theControl).contrlRect.top;
  781.         theTextRect.top = theTextRect.bottom - ( theFInfo.ascent + theFInfo.descent );
  782.         
  783.     } else {
  784.     
  785.         // Calculate the thumb center.
  786.         theThumbCenter = wThumbRect.top+(wThumbRect.bottom-wThumbRect.top)/2;
  787.  
  788.         // Calculate the horizontal position of the text.
  789.         theHPos = (**theControl).contrlRect.right + 2;
  790.         
  791.         // Calculate the vertical position of the text.
  792.         theVPos = theThumbCenter + ( theFInfo.ascent+theFInfo.descent )/2;
  793.  
  794.         // Calculate the left side of the text rectangle.
  795.         theTextRect.left = theHPos;
  796.         
  797.         // Calculate the right side of the text rectangle plus an extra character.
  798.         theTextRect.right = theTextRect.left + theStringWidth + theFInfo.widMax;
  799.         
  800.     }
  801.         
  802.     // Clip to this rectangle.
  803.     newClip = NewRgn();
  804.     RectRgn( newClip, &theTextRect );
  805.     SectRgn( newClip, savedClip, newClip );
  806.     
  807.     if ( !EmptyRgn( newClip ) ) {
  808.  
  809.         // Set the new clip region.
  810.         SetClip( newClip );
  811.  
  812.         // Erase the text rect.
  813.         EraseRect( &theTextRect );
  814.  
  815.         // Find out the slider's activation state set the text color.
  816.         if ( inDepth == 1 ) {
  817.  
  818.             ForeColor( blackColor );
  819.             BackColor( whiteColor );
  820.  
  821.         } else {
  822.  
  823.             // Get the aux control record.
  824.             GetAuxiliaryControlRecord( theControl, &acHndl );
  825.             
  826.             // Get the color table.
  827.             acCTable = (**acHndl).acCTable;
  828.  
  829.             if ( IsSliderActive( theControl ) ) {
  830.                 theColor = ((**acCTable).ctTable[2]).rgb;
  831.                 RGBForeColor( &theColor );
  832.             } else {
  833.                 theColor.red = theColor.green = theColor.blue = 0x8888;
  834.                 RGBForeColor( &theColor );
  835.             }
  836.         }
  837.  
  838.         // Move to the text position.
  839.         MoveTo( theHPos, theVPos );
  840.  
  841.         // Draw the text.
  842.         DrawString( theValueString );
  843.  
  844.         // To dim out a black and white slider's value, paint a
  845.         // gray patterned rectangle over it.
  846.         if ( inDepth == 1 && !IsSliderActive( theControl ) ) {
  847.             PenNormal();
  848.             GetIndPattern( &grayPattern, sysPatListID, 4 );
  849.             PenPat( &grayPattern );
  850.             PenMode( patBic );
  851.             PaintRect( &theTextRect );
  852.         }            
  853.     }
  854.     
  855.     // Restore the clipping region.
  856.     SetClip( savedClip );
  857.     DisposeRgn( savedClip );
  858.     DisposeRgn( newClip );
  859.     
  860.     // Restore the font characteristics.
  861.     ((GrafPtr)theWindow)->txFont = savedTextFont;
  862.     ((GrafPtr)theWindow)->txFace = savedTextStyle;
  863.     ((GrafPtr)theWindow)->txSize = savedTextSize;
  864. }
  865.  
  866.  
  867. // ---------------------------------------------------------------------------------
  868. //    • SliderTestControl
  869. // ---------------------------------------------------------------------------------
  870. //    Finds the part, if any, that the point is in.
  871.  
  872. long
  873. SliderTestControl( short inVarCode, ControlHandle inControl, long inParam )
  874. {
  875.     Rect        theRect;
  876.     Point        thePt;
  877.     long        theResult = 0L;
  878.  
  879.     // The test point is contained in inParam.
  880.     thePt.v = HiWord( inParam );
  881.     thePt.h = LoWord( inParam );
  882.     
  883.     // Get the drag rect.
  884.     CalcDragRect( inControl, &theRect );
  885.     
  886.     // Test if the point is in the drag.
  887.     if ( PtInRect( thePt, &theRect ) ) {
  888.         
  889.         // Get the thumb rectangle (window coordinates).
  890.         CalcThumbRect( inControl, &theRect );
  891.         
  892.         if ( PtInRect( thePt, &theRect ) ) {
  893.         
  894.             // Point was in the thumb.
  895.             theResult = inThumb;
  896.         
  897.         } else {
  898.  
  899.             // Point was in page up or down area.
  900.             if ( IsHorizontal( inControl ) ) {
  901.                 theResult = ( thePt.h < theRect.left ) ? inPageDown : inPageUp;
  902.             } else { 
  903.                 theResult = ( thePt.v < theRect.top ) ? inPageDown : inPageUp;
  904.             }
  905.  
  906.         }
  907.     }
  908.         
  909.     return theResult;
  910. }
  911.  
  912.  
  913. #define kCalcCRgnsMask        (0x7FFFFFFFL)
  914.  
  915. // ---------------------------------------------------------------------------------
  916. //    • SliderCalcCRgns
  917. // ---------------------------------------------------------------------------------
  918. //    Calculates either the entire control's region which is just the control rectangle for
  919. //    the slider control or the indicator's region, depending on the high bit of inParam.
  920. //    Note: the calcCRgns message is only sent by the control manager when running
  921. //    in 24-bit mode. Under 32-bit mode, two messages are sent: one for the entire
  922. //    control (calcCntlRgn), and one for the indicator (calcThumbRgn).
  923.  
  924. void
  925. SliderCalcCRgns( short inVarCode, ControlHandle inControl, long inParam )
  926. {
  927.     // Get the control rectangle.
  928.     Rect    theRect = (**inControl).contrlRect;
  929.     
  930.     // Since we're in 24-bit mode, make sure the high
  931.     // order bits of inParam are clear.
  932.     inParam &= kCalcCRgnsMask;
  933.     
  934.     // Put the control region into inParam.
  935.     RectRgn( (RgnHandle) inParam, &theRect );
  936. }
  937.  
  938. #undef kCalcCRgnsMask
  939.  
  940.  
  941. // ---------------------------------------------------------------------------------
  942. //    • SliderInitControl
  943. // ---------------------------------------------------------------------------------
  944. //    Performs initialization for the control definition function such as putting some
  945. //    data in the contrlData or contrlRfCon fields or setting the contrlAction field to
  946. //    (Ptr) -1L so that the control manager will call the CDEF with the autoTrack
  947. //    message.
  948.  
  949. void
  950. SliderInitControl( short inVarCode, ControlHandle inControl, long inParam )
  951. {
  952.     SliderDataHnd    theSliderData;
  953.     
  954.     // Indicate that auto-tracking is requested.
  955.     (**inControl).contrlAction = (ControlActionUPP) -1L;
  956.     
  957.     // Allocate the slider's data and place a handle to the data structure
  958.     // in the contrlData field.
  959.     theSliderData = (SliderDataHnd) NewHandleClear( sizeof( SliderData ) );
  960.     
  961.     if ( theSliderData != nil ) {
  962.  
  963.         OSErr    err;
  964.         short    thePosition;
  965.     
  966.         // Stuff the slider data handle in the control's contrlData field.        
  967.         (**inControl).contrlData  = (Handle) theSliderData;
  968.  
  969.         // Store the current depth.
  970.         (**theSliderData).sdDepth = GetDepth( inControl );
  971.         
  972.         // Store the variation code.
  973.         (**theSliderData).sdVarCode = inVarCode;
  974.         
  975.         // Calculate the new thumb rect and put it in the slider's data.
  976.         thePosition = CalcPosition( inControl );
  977.         (**theSliderData).sdPosition = thePosition;
  978.  
  979.         // Create the offscreen graphics world for the slider.
  980.         err = SliderCreateOffscreenWorld( inControl );
  981.  
  982.         if ( err != noErr ) {
  983.  
  984.             // Initialization failed!
  985.             DisposeHandle( (Handle) theSliderData );
  986.             (**inControl).contrlData = nil;
  987.             
  988.         }
  989.     }
  990. }
  991.  
  992.  
  993. // ---------------------------------------------------------------------------------
  994. //    • SliderCreateOffscreenWorld
  995. // ---------------------------------------------------------------------------------
  996. //    Create the offscreen graphics world for the slider control.
  997.  
  998. OSErr
  999. SliderCreateOffscreenWorld( ControlHandle inControl )
  1000. {
  1001.     OSErr        err = noErr;
  1002.     SliderDataHnd    theSliderData;
  1003.     
  1004.     // Get the handle to the slider's data and lock it.
  1005.     theSliderData = GetSliderData( inControl );
  1006.     
  1007.     if ( theSliderData != nil ) {
  1008.     
  1009.         CGrafPtr    theSavePort;
  1010.         GDHandle    theSaveDevice;
  1011.         Rect        theRect;
  1012.  
  1013.         // Save the current port and device.
  1014.         GetGWorld( &theSavePort, &theSaveDevice );
  1015.  
  1016.         // Lock the slider data.
  1017.         HLock( (Handle) theSliderData );
  1018.     
  1019.         // Calculate the track rect.
  1020.         CalcTrackRect( inControl, &theRect );
  1021.         
  1022.         // Offset to (0,0) and store in the slider data.
  1023.         SetRect( &(**theSliderData).sdTrackBounds, 0, 0, theRect.right-theRect.left,
  1024.             theRect.bottom-theRect.top );
  1025.             
  1026.         // Create the track gworld.
  1027.         err = NewGWorld( &(**theSliderData).sdTrack, 0,
  1028.             &(**theSliderData).sdTrackBounds, nil, nil, 0 );
  1029.         if ( err != noErr || (**theSliderData).sdTrack == nil ) return err;
  1030.  
  1031.         // Erase the new gworld.
  1032.         SetGWorld( (**theSliderData).sdTrack, nil );
  1033.         LockPixels( GetGWorldPixMap( (**theSliderData).sdTrack ) );
  1034.         EraseRect( &(**theSliderData).sdTrackBounds );
  1035.         UnlockPixels( GetGWorldPixMap( (**theSliderData).sdTrack ) );
  1036.     
  1037.         // Calculate the thumb rect.
  1038.         CalcThumbRect( inControl, &theRect );
  1039.         
  1040.         // Offset to (0,0) and store in the slider data.
  1041.         SetRect( &(**theSliderData).sdThumbBounds, 0, 0, theRect.right-theRect.left,
  1042.             theRect.bottom-theRect.top );
  1043.             
  1044.         // Create the thumb gworld.
  1045.         err = NewGWorld( &(**theSliderData).sdThumb, 0,
  1046.             &(**theSliderData).sdThumbBounds, nil, nil, 0 );
  1047.         if ( err != noErr || (**theSliderData).sdThumb == nil ) {
  1048.             DisposeGWorld( (**theSliderData).sdTrack );
  1049.             return err;
  1050.         }
  1051.         
  1052.         // Erase the new gworld.
  1053.         SetGWorld( (**theSliderData).sdThumb, nil );
  1054.         LockPixels( GetGWorldPixMap( (**theSliderData).sdThumb ) );
  1055.         EraseRect( &(**theSliderData).sdThumbBounds );
  1056.         UnlockPixels( GetGWorldPixMap( (**theSliderData).sdThumb ) );
  1057.     
  1058.         // Unlock the slider's data.
  1059.         HUnlock( (Handle) theSliderData );
  1060.  
  1061.         // Restore the current port and device.
  1062.         SetGWorld( theSavePort, theSaveDevice );
  1063.  
  1064.     }
  1065.     
  1066.     return err;
  1067. }
  1068.  
  1069.  
  1070. // ---------------------------------------------------------------------------------
  1071. //    • SliderDisposeControl
  1072. // ---------------------------------------------------------------------------------
  1073. //    This routine is called in response to the dispCntl message
  1074. //    so that the CDEF may do additional disposal actions.
  1075.  
  1076. void
  1077. SliderDisposeControl( short inVarCode, ControlHandle inControl, long inParam )
  1078. {
  1079.     if ( inControl != nil ) {
  1080.     
  1081.         // Get the handle to the slider's data.
  1082.         SliderDataHnd    theSliderData = GetSliderData( inControl );
  1083.         
  1084.         if ( theSliderData != nil ) {
  1085.         
  1086.             // Lock the slider data.
  1087.             HLock( (Handle) theSliderData );
  1088.         
  1089.             // Destroy the track offscreen graphics world.
  1090.             if ( (**theSliderData).sdTrack  != nil ) {
  1091.                 DisposeGWorld( (**theSliderData).sdTrack );
  1092.             }
  1093.  
  1094.             // Destroy the thumb offscreen graphics world.
  1095.             if ( (**theSliderData).sdThumb  != nil ) {
  1096.                 DisposeGWorld( (**theSliderData).sdThumb );
  1097.             }
  1098.  
  1099.             // Dispose of the slider's private data.
  1100.             DisposeHandle(     (Handle) theSliderData );
  1101.             (**inControl).contrlData = nil;
  1102.         }
  1103.     }
  1104. }
  1105.  
  1106.  
  1107. // ---------------------------------------------------------------------------------
  1108. //    • SliderDragControl
  1109. // ---------------------------------------------------------------------------------
  1110. //    Perform custom dragging of the control thumb.
  1111.  
  1112. long
  1113. SliderDragControl( short inVarCode, ControlHandle inControl, long inParam )
  1114. {
  1115.     SliderDataHnd            theSliderData;
  1116.     WindowPtr            theWindow;
  1117.     RgnHandle                theVisRgn;
  1118.     RGBColor                theForeColor;
  1119.     RGBColor                theBackColor;
  1120.     DeviceLoopDrawingUPP    theDeviceLoopUPP;
  1121.     Point                    theStartPt;
  1122.     Point                    theMousePt;
  1123.     short                theRange;
  1124.     Rect                    theDragRect;
  1125.     Rect                    theThumbRect;
  1126.     Rect                    theWTrackPiece;
  1127.     Rect                    theGWTrackPiece;
  1128.     Rect                    theSlopRect;
  1129.     short                theLimit = 0;
  1130.     short                theDragLimit;
  1131.     short                theOffset;
  1132.     short                thePosition;
  1133.     short                theOriginalPosition;
  1134.     short                thePinPt;
  1135.     short                theSetPt;
  1136.     short                theValue;
  1137.     Boolean                isHorizontal;
  1138.  
  1139.     // Get out now if theControl is nil.
  1140.     if ( inControl == nil ) return 0L;
  1141.  
  1142.     // Get the slider's data.
  1143.     theSliderData = GetSliderData( inControl );
  1144.     if ( theSliderData == nil ) return 0L;
  1145.     
  1146.     // Get the starting mouse location.
  1147.     GetMouse( &theStartPt );
  1148.  
  1149.     // Calculate the thumb rect.
  1150.     CalcThumbRect( inControl, &theThumbRect );
  1151.     
  1152.     // Check if the start point is in the thumb.
  1153.     if ( !PtInRect( theStartPt, &theThumbRect ) ) return 0L;
  1154.                                 
  1155.     // Lock the slider's data.
  1156.     HLock( (Handle) theSliderData );
  1157.                                 
  1158.     // Calculate the drag rect.
  1159.     CalcDragRect( inControl, &theDragRect );
  1160.  
  1161.     // Calculate the slop rect.
  1162.     theSlopRect = theDragRect;
  1163.     InsetRect( &theSlopRect, -20, -20 );
  1164.  
  1165.     // Store the original position for redrawing when outside the slop rect.
  1166.     theOriginalPosition = (**theSliderData).sdPosition;
  1167.  
  1168.     // Get the control's parent window.
  1169.     theWindow = (WindowPtr) (**inControl).contrlOwner;
  1170.     SetPort( theWindow );
  1171.  
  1172.     // Save the foreground and background colors.
  1173.     GetForeColor( &theForeColor );
  1174.     GetBackColor( &theBackColor );
  1175.     
  1176.     // Create the DeviceLoopDrawingUPP for drawing control values.
  1177.     theDeviceLoopUPP = NewDeviceLoopDrawingProc( SliderDrawValue );
  1178.     
  1179.     // Find out if the slider is horizontal or vertical.
  1180.     isHorizontal = IsHorizontal( inControl );
  1181.     
  1182.     // Calcuate the range of allowed control values.
  1183.     theRange = (**inControl).contrlMax - (**inControl).contrlMin;
  1184.     
  1185.     // Calculate dragging parameters.
  1186.     if ( isHorizontal ) {
  1187.         theDragLimit = (theDragRect.right-theDragRect.left) -
  1188.             (theThumbRect.right-theThumbRect.left);
  1189.         thePinPt = theThumbRect.right - theStartPt.h;
  1190.     } else {
  1191.         theDragLimit = (theDragRect.bottom-theDragRect.top) -
  1192.             (theThumbRect.bottom-theThumbRect.top);
  1193.         thePinPt = theThumbRect.bottom - theStartPt.v;
  1194.     }
  1195.     
  1196.     // Loop while the mouse is down.
  1197.     while( StillDown() ) {
  1198.         
  1199.         // Get the mouse location.
  1200.         GetMouse( &theMousePt );
  1201.         
  1202.         // Calculate the offset.
  1203.         if( !isHorizontal ) {
  1204.             theOffset = theMousePt.v - theStartPt.v;
  1205.         } else {
  1206.             theOffset = theMousePt.h - theStartPt.h;
  1207.         }
  1208.         
  1209.         // Check if the control was in a limit.
  1210.         if ( theLimit != 0 ) {
  1211.  
  1212.             // Calculate the thumb rect.
  1213.             CalcThumbRect( inControl, &theThumbRect );
  1214.             
  1215.             // Calculate the present offset of the cursor
  1216.             // in the thumb.
  1217.             if ( isHorizontal ) {
  1218.                 theSetPt = theThumbRect.right - theMousePt.h;
  1219.             } else {
  1220.                 theSetPt = theThumbRect.bottom - theMousePt.v;
  1221.             }
  1222.             
  1223.             if ( theLimit > 0 ) {
  1224.                 // The thumb was in the max limit.
  1225.                 if ( theSetPt < thePinPt ) {
  1226.                     // Thumb is still in the limit.
  1227.                     theOffset = 0;
  1228.                 } else {
  1229.                     // No longer in limit.
  1230.                     theLimit = 0;
  1231.                     theOffset = thePinPt-theSetPt;
  1232.                 }
  1233.             } else {
  1234.                 // The thumb was in the min limit.
  1235.                 if ( theSetPt > thePinPt ) {
  1236.                     // Thumb is still in the limit.
  1237.                     theOffset = 0;
  1238.                 } else {
  1239.                     // No longer in limit.
  1240.                     theLimit = 0;
  1241.                     theOffset = thePinPt-theSetPt;
  1242.                 }
  1243.             }
  1244.  
  1245.         }
  1246.  
  1247.         if ( theOffset != 0 ) {
  1248.  
  1249.             // Calculate the new position.
  1250.             thePosition = (**theSliderData).sdPosition + theOffset;
  1251.  
  1252.             // Calculate the new value.
  1253.             theValue = (float) thePosition / (float) theDragLimit * theRange +
  1254.                 (**inControl).contrlMin;
  1255.  
  1256.             // Clamp the position inside the drag area,
  1257.             // and set the limit flag.
  1258.             if ( thePosition > theDragLimit ) {
  1259.                 thePosition = theDragLimit;
  1260.                 theLimit = 1;
  1261.             } else if ( thePosition < 0 ) {
  1262.                 thePosition = 0;
  1263.                 theLimit = -1;
  1264.             }
  1265.             
  1266.             // Clamp the control value.
  1267.             if ( theValue > (**inControl).contrlMax ) {
  1268.                 theValue = (**inControl).contrlMax;
  1269.             } else if ( theValue < (**inControl).contrlMin ) {
  1270.                 theValue = (**inControl).contrlMin;
  1271.             }
  1272.  
  1273.             // Check if the position has actually changed.
  1274.             if ( (**theSliderData).sdPosition != thePosition ) {
  1275.                 
  1276.                 // Calculate the thumb rect at the old position.
  1277.                 CalcThumbRect( inControl, &theWTrackPiece );
  1278.                 
  1279.                 // Update the position and control value.
  1280.                 (**theSliderData).sdPosition = thePosition;
  1281.                 (**inControl).contrlValue = theValue;
  1282.  
  1283.                 // Calculate the thumb rect at the new position.
  1284.                 CalcThumbRect( inControl, &theThumbRect );
  1285.  
  1286.                 // Calculate the rectangle that will cover the old thumb position.                
  1287.                 if ( isHorizontal ) {
  1288.                     if ( theThumbRect.left < theWTrackPiece.right &&
  1289.                         theThumbRect.right > theWTrackPiece.right ) {
  1290.                             theWTrackPiece.right = theThumbRect.left;
  1291.                     } else if  ( theThumbRect.right > theWTrackPiece.left &&
  1292.                         theThumbRect.left < theWTrackPiece.left ) {
  1293.                             theWTrackPiece.left = theThumbRect.right;
  1294.                     }
  1295.                 } else {
  1296.                     if ( theThumbRect.top < theWTrackPiece.bottom &&
  1297.                         theThumbRect.bottom > theWTrackPiece.bottom ) {
  1298.                             theWTrackPiece.bottom = theThumbRect.top;
  1299.                     } else if  ( theThumbRect.bottom > theWTrackPiece.top &&
  1300.                         theThumbRect.top < theWTrackPiece.top ) {
  1301.                             theWTrackPiece.top = theThumbRect.bottom;
  1302.                     }
  1303.                 }
  1304.                 
  1305.                 // Transform the rect into gworld coordinates.
  1306.                 theGWTrackPiece = theWTrackPiece;
  1307.                 OffsetRect( &theGWTrackPiece, -(**inControl).contrlRect.left,
  1308.                     -(**inControl).contrlRect.top );
  1309.  
  1310.                 // Set the foreground and background colors for CopyBits.
  1311.                 ForeColor( blackColor );
  1312.                 BackColor( whiteColor );
  1313.             
  1314.                 // Copy a piece of the track to the window to cover
  1315.                 // the previous thumb position.
  1316.                 CopyBits( &((GrafPtr)(**theSliderData).sdTrack)->portBits,
  1317.                     &((GrafPtr)theWindow)->portBits,
  1318.                     &theGWTrackPiece, &theWTrackPiece, srcCopy, nil );
  1319.             
  1320.                 // Copy the thumb to the window.
  1321.                 CopyBits( &((GrafPtr)(**theSliderData).sdThumb)->portBits,
  1322.                     &((GrafPtr)theWindow)->portBits,
  1323.                     &(**theSliderData).sdThumbBounds,
  1324.                     &theThumbRect, srcCopy, nil );
  1325.  
  1326.                 if ( (**theSliderData).sdVarCode & kValueAboveCtl ) {
  1327.                     
  1328.                     // Restore the foreground and background colors.
  1329.                     RGBForeColor( &theForeColor );
  1330.                     RGBBackColor( &theBackColor );
  1331.     
  1332.                     // Draw the control value.
  1333.                     theVisRgn = ((GrafPtr)(**inControl).contrlOwner)->visRgn;
  1334.                     DeviceLoop( theVisRgn, theDeviceLoopUPP, (long) inControl, 0 );
  1335.                 }
  1336.             }
  1337.         }
  1338.         
  1339.         // Update the start point.
  1340.         theStartPt = theMousePt;
  1341.     }
  1342.  
  1343.     // Unlock the slider's data.
  1344.     HUnlock( (Handle) theSliderData );
  1345.  
  1346.     // Dispose of the DeviceLoopDrawingUPP.
  1347.     DisposeRoutineDescriptor( theDeviceLoopUPP );
  1348.  
  1349.     // Restore the foreground and background colors.
  1350.     RGBForeColor( &theForeColor );
  1351.     RGBBackColor( &theBackColor );
  1352.     
  1353.     // Return a non-zero value to indicate dragging was handled by the CDEF.
  1354.     return 1L;
  1355. }
  1356.  
  1357.  
  1358. // ---------------------------------------------------------------------------------
  1359. //    • SliderAutoTrack
  1360. // ---------------------------------------------------------------------------------
  1361. //    Adjust the control's value when clicked in the up or down arrows or the page-up
  1362. //    or page-down areas. TrackControl must be called with a value of -1 for the
  1363. //    action procedure to receive auto-tracking.
  1364.  
  1365. void
  1366. SliderAutoTrack( short inVarCode, ControlHandle inControl, long inParam )
  1367. {
  1368.     SliderDataHnd    theSliderData;
  1369.     short        theRange;
  1370.     short        theValue;
  1371.     short        theOffset = 0;
  1372.     short        theNewPosition;
  1373.  
  1374.     // Get the slider's data.
  1375.     theSliderData = GetSliderData( inControl );
  1376.     if ( theSliderData == nil ) return;
  1377.     
  1378.     // Calculate the range of control values.
  1379.     theRange = (**inControl).contrlMax - (**inControl).contrlMin;
  1380.  
  1381.     // Calculate how much too move the control.
  1382.     switch ( inParam ) {
  1383.         case inUpButton:
  1384.             theOffset = 1;
  1385.             break;
  1386.         case inDownButton:
  1387.             theOffset = -1;
  1388.             break;
  1389.         case inPageUp:
  1390.             theOffset = theRange / 10;
  1391.             if ( theOffset == 0 ) theOffset = 1;
  1392.             break;
  1393.         case inPageDown:
  1394.             theOffset = - theRange / 10;
  1395.             if ( theOffset == 0 ) theOffset = -1;
  1396.             break;
  1397.     }
  1398.  
  1399.     if ( theOffset != 0 ) {
  1400.     
  1401.         // Calculate the new control value.
  1402.         theValue = (**inControl).contrlValue;
  1403.         theValue += theOffset;
  1404.         
  1405.         // Make sure the limits are not exceeded.
  1406.         if ( theValue < (**inControl).contrlMin ) theValue = (**inControl).contrlMin;
  1407.         if ( theValue > (**inControl).contrlMax ) theValue = (**inControl).contrlMax;
  1408.     
  1409.         // Set the new control value.
  1410.         (**inControl).contrlValue = theValue;
  1411.         
  1412.         // Calculate the new thumb rect and put it in the slider's data.
  1413.         theNewPosition = CalcPosition( inControl );
  1414.         (**theSliderData).sdPosition = theNewPosition;
  1415.     
  1416.         // Redraw the control.
  1417.         SliderDrawControl( inVarCode, inControl, 0 );
  1418.     }
  1419. }
  1420.  
  1421.  
  1422. // ---------------------------------------------------------------------------------
  1423. //    • SliderCalcCntlRgn
  1424. // ---------------------------------------------------------------------------------
  1425. //    Calculates the entire control's region which is just the control rectangle for
  1426. //    the slider control. Note: the calcCntlRgn message is only sent by the control
  1427. //    manager when running in 32-bit mode. Under 24-bit mode the single message
  1428. //    calcCRgns is sent for all region calculations.
  1429.  
  1430. void
  1431. SliderCalcCntlRgn( short inVarCode, ControlHandle theControl, long inParam )
  1432. {
  1433.     // Get the control rect.
  1434.     Rect    contrlRect = (**theControl).contrlRect;
  1435.  
  1436.     // Put a handle to the slider's control region into inParam.
  1437.     RectRgn( (RgnHandle) inParam, &contrlRect );
  1438. }
  1439.  
  1440.  
  1441. // ---------------------------------------------------------------------------------
  1442. //    • SliderCalcThumbRgn
  1443. // ---------------------------------------------------------------------------------
  1444. //    Calculates the entire indicator's region.  Note: the calcCntlRgn message is only
  1445. //    sent by the control manager when running in 32-bit mode. Under 24-bit mode
  1446. //    the single message calcCRgns is sent for all region calculations.
  1447.  
  1448. void
  1449. SliderCalcThumbRgn( short inVarCode, ControlHandle inControl, long inParam )
  1450. {
  1451.     // Get a handle to the slider's private data.
  1452.     SliderDataHnd    theSliderData = GetSliderData( inControl );
  1453.     
  1454.     if ( theSliderData ) {
  1455.         
  1456.         Rect    theThumbRect;
  1457.  
  1458.         // Get the thumb's rect.
  1459.         CalcThumbRect( inControl, &theThumbRect );
  1460.         
  1461.         // Give the control manager a handle to the thumb's region.
  1462.         RectRgn( (RgnHandle) inParam, &theThumbRect );
  1463.     }
  1464. }
  1465.  
  1466.  
  1467. // ---------------------------------------------------------------------------------
  1468. //    • CalcPosition
  1469. // ---------------------------------------------------------------------------------
  1470. //    Calculate the thumb position.
  1471.  
  1472. short
  1473. CalcPosition( ControlHandle inControl )
  1474. {
  1475.     Rect        theDragRect;
  1476.     Rect        theThumbRect;
  1477.     short    theDragLimit = 0;
  1478.     short    thePosition;
  1479.  
  1480.     // Calculate the drag rect.
  1481.     CalcDragRect( inControl, &theDragRect );
  1482.  
  1483.     // Calculate the thumb rect. Actually, all that is needed is the
  1484.     // thumb size. If the thumb size were variable width, this routine
  1485.     // wouldn't need to be modified.
  1486.     CalcThumbRect( inControl, &theThumbRect );
  1487.     
  1488.     // Calculate the drag limit.
  1489.     if ( IsHorizontal( inControl ) ) {
  1490.         theDragLimit = (theDragRect.right - theDragRect.left) -
  1491.             (theThumbRect.right - theThumbRect.left);
  1492.     } else {
  1493.         theDragLimit = (theDragRect.bottom - theDragRect.top) -
  1494.             (theThumbRect.bottom - theThumbRect.top);
  1495.     }
  1496.     
  1497.     // Calculate the thumb position.
  1498.     thePosition = theDragLimit *
  1499.         (float) ((**inControl).contrlValue-(**inControl).contrlMin) /
  1500.         (float) ((**inControl).contrlMax-(**inControl).contrlMin);
  1501.     if ( thePosition < 0 ) {
  1502.         thePosition = 0;
  1503.     } else if ( thePosition > theDragLimit ) {
  1504.         thePosition = theDragLimit;
  1505.     }
  1506.     
  1507.     return thePosition;
  1508. }
  1509.  
  1510.  
  1511. // ---------------------------------------------------------------------------------
  1512. //    • CalcDragRect
  1513. // ---------------------------------------------------------------------------------
  1514. //    Calculate the drag rectangle. This is the track rectangle minus any highlights.
  1515.  
  1516. void
  1517. CalcDragRect( ControlHandle inControl, Rect *outRect )
  1518. {
  1519.     // Calculate the track rect.
  1520.     CalcTrackRect( inControl, outRect );
  1521.     
  1522.     // Inset the rect to account for the highlights.
  1523.     InsetRect( outRect, 1, 1 );
  1524. }
  1525.  
  1526.  
  1527. // ---------------------------------------------------------------------------------
  1528. //    • CalcTrackRect
  1529. // ---------------------------------------------------------------------------------
  1530. //    Calculate the track rectangle.
  1531.  
  1532. void
  1533. CalcTrackRect( ControlHandle inControl, Rect *outRect )
  1534. {
  1535.     // The track rect is (for now) the same as the control rect.
  1536.     // If the control had arrows on the end they would be subtracted
  1537.     // from the control rect here.
  1538.     *outRect = (**inControl).contrlRect;
  1539. }
  1540.  
  1541.  
  1542. // ---------------------------------------------------------------------------------
  1543. //    • CalcThumbRect
  1544. // ---------------------------------------------------------------------------------
  1545. //    Calculate the thumb rectangle based on the track rectangle and the current position.
  1546.  
  1547. void
  1548. CalcThumbRect( ControlHandle inControl, Rect *outRect )
  1549. {
  1550.     // Get a handle to the slider's private data.
  1551.     SliderDataHnd    theSliderData = GetSliderData( inControl );
  1552.     
  1553.     if ( theSliderData != nil ) {
  1554.     
  1555.         // Calculate the drag rectangle.
  1556.         CalcDragRect( inControl, outRect );
  1557.         
  1558.         if ( IsHorizontal( inControl ) ) {
  1559.         
  1560.             outRect->left = outRect->left + (**theSliderData).sdPosition;
  1561.             outRect->right = outRect->left + kStdThumbSize;
  1562.         
  1563.         } else {
  1564.  
  1565.             outRect->top = outRect->top + (**theSliderData).sdPosition;
  1566.             outRect->bottom = outRect->top + kStdThumbSize;
  1567.         
  1568.         }        
  1569.     }
  1570. }
  1571.  
  1572.  
  1573. // ---------------------------------------------------------------------------------
  1574. //    • IsHorizontal
  1575. // ---------------------------------------------------------------------------------
  1576. //    Looks at the control's contrlRect to see whether its width is greater than its height.
  1577. //    If true, the control is horizontal, otherwise it's vertical.
  1578.  
  1579. Boolean
  1580. IsHorizontal( ControlHandle inControl )
  1581. {
  1582.     // Make a local copy of the control's rect.
  1583.     Rect    theRect = (**inControl).contrlRect;
  1584.     
  1585.     // Compare the control's width to its height.
  1586.     return ( (theRect.right-theRect.left) > (theRect.bottom-theRect.top) ) ? true : false;
  1587. }
  1588.  
  1589.  
  1590. // ---------------------------------------------------------------------------------
  1591. //    • GetDepth
  1592. // ---------------------------------------------------------------------------------
  1593. //     Find the depth of the control's port.
  1594.  
  1595. short
  1596. GetDepth( ControlHandle theControl )
  1597. {
  1598.     if ( ((CGrafPtr)(**theControl).contrlOwner)->portVersion == ((short)0xC000) ) {
  1599.         return (**((CGrafPtr)(**theControl).contrlOwner)->portPixMap).pixelSize;
  1600.     } else {
  1601.         return 1;
  1602.     }
  1603. }
  1604.  
  1605.  
  1606. // ---------------------------------------------------------------------------------
  1607. //        • MixColors
  1608. // ---------------------------------------------------------------------------------
  1609. //    Mix two rgb colors to a specific shade (0-15).
  1610. //    Borrowed from the Infinity Windoid by Troy Gaul.
  1611.  
  1612. void
  1613. MixColors( const RGBColor *inLight, const RGBColor *inDark, 
  1614.     short inShade, RGBColor *outResult )
  1615. {
  1616.     // This is necessary because we give shades between light and
  1617.     // dark (0% is light), but for colors, $0000 is black and $FFFF 
  1618.     // is dark.
  1619.     inShade = 0x0F - inShade;
  1620.  
  1621.     // Calculate the mixed color.
  1622.     outResult->red = (long) (inLight->red - inDark->red) *
  1623.         inShade / 15 + inDark->red;
  1624.     outResult->green = (long) (inLight->green - inDark->green) *
  1625.         inShade / 15 + inDark->green;
  1626.     outResult->blue = (long) (inLight->blue - inDark->blue) *
  1627.         inShade / 15 + inDark->blue;
  1628. }
  1629.  
  1630.  
  1631. // ---------------------------------------------------------------------------------
  1632. //    • CopyPStr
  1633. // ---------------------------------------------------------------------------------
  1634. //    Copy the source string into the destination string.
  1635.  
  1636. StringPtr
  1637. CopyPStr( ConstStr255Param inSourceStr, Str255 outDestStr )
  1638. {
  1639.     // Copy the source string into the dest string.
  1640.     BlockMoveData( inSourceStr, outDestStr, *inSourceStr + 1 );
  1641.     
  1642.     return outDestStr;
  1643. }
  1644.  
  1645.  
  1646. // ---------------------------------------------------------------------------------
  1647. //    • ConcatPStr
  1648. // ---------------------------------------------------------------------------------
  1649. //    Concatenate the second string to the first string.
  1650.  
  1651. StringPtr
  1652. ConcatPStr( Str255 ioFirstStr, ConstStr255Param inSecondStr )
  1653. {
  1654.     // Limit the combined string to 255 chars.
  1655.     Size    theNumChars = *inSecondStr;
  1656.     if ( *ioFirstStr + theNumChars > 255 ) {
  1657.         theNumChars = 255 - *ioFirstStr;
  1658.     }
  1659.     
  1660.     // Copy second string to end of first string
  1661.     BlockMoveData( inSecondStr + 1, ioFirstStr + *ioFirstStr + 1, theNumChars );
  1662.  
  1663.     // Set length of combined string
  1664.     *ioFirstStr += theNumChars;
  1665.  
  1666.     return ioFirstStr;
  1667. }
  1668.  
  1669.  
  1670. // ---------------------------------------------------------------------------------
  1671. //    • PrefixPStr
  1672. // ---------------------------------------------------------------------------------
  1673. //    Insert the second string before the first string.
  1674.  
  1675. StringPtr
  1676. PrefixPStr( Str255 ioFirstStr, ConstStr255Param inSecondStr )
  1677. {
  1678.     // Limit the combined string to 255 chars.
  1679.     Size    theNumChars = *inSecondStr;
  1680.     if ( *ioFirstStr + theNumChars > 255 ) {
  1681.         theNumChars = 255 - *ioFirstStr;
  1682.     }
  1683.     
  1684.     // Move the first string to make room for the prefix.
  1685.     BlockMoveData( ioFirstStr + 1,  ioFirstStr + theNumChars + 1, *ioFirstStr );
  1686.     
  1687.     // Move the second string into the first.
  1688.     BlockMoveData( inSecondStr + 1, ioFirstStr + 1, theNumChars );
  1689.     
  1690.     // Set length of combined string
  1691.     *ioFirstStr += theNumChars;
  1692.  
  1693.     return ioFirstStr;
  1694. }
  1695.